// Copyright (c) 2010, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// ---
// Author: Jeffrey Junfeng Pan (panjunfeng@gmail.com)

// The command line tool for playing with the frequent pattern mining algorithm.

#include "association_rules.h"
#include "base.h"
#include "common/logging.h"
#include "common/strutil.h"
#include "tree.h"

// minimal support via count in [1, number of total itemsets]
// if both min_support_ratio and min_support_count are set
// the ratio will be used
#define FLAGS_min_support_count 1

// minimal support via ratio in [0, 1]
// if both min_support_ratio and min_support_count are set"
// the ratio will be used
#define FLAGS_min_support_ratio -1

// minimal confidence via count in [1, number of total itemsets]
// if both min_confidence_ratio and min_confidence_count are set
// the ratio will be used
#define FLAGS_min_confidence_count 1

// minimal confidence via ratio in [0, 1]
// if both min_confidence_ratio and min_confidence_count are set
// the ratio will be used
#define FLAGS_min_confidence_ratio -1

// type of item, which can be string or integer
#define FLAGS_item_type "string"

// run association rule mining or not
#define FLAGS_run_association_rule false

// Print the input itemsets or not
#define FLAGS_print_input true

// Print the frequent pattern tree
#define FLAGS_print_tree true

namespace frequent_pattern_mining {

// Mining Frequent Patterns when the items are strings.
void StringFrequentPatternMining(const vector<string>& string_list) {
  // Construct transactions.
  Tree<string> tree("THIS_ITEM_NEVER_EXISTS_IN_TRANSACTIONS");
  TransactionsMemoryImpl<string> transactions;
  for (int i = 0; i < string_list.size(); ++i) {
    vector<string> transaction;
    string line = string_list[i];
    SplitStringUsing(line, " ", &transaction);
    transactions.push_back(transaction);
  }

  // Build Frequent Pattern Tree
  if (FLAGS_min_support_ratio >= 0) {
    tree.BuildTree(&transactions, -1, FLAGS_min_support_ratio);
  } else {
    tree.BuildTree(&transactions, FLAGS_min_support_count, -1);
  }

  // Output tree info
  if (FLAGS_print_tree) {
    cout << "\nThe dictionary" << endl;
    cout << tree.DictionaryToString();
    cout << "\nThe frequent pattern tree" << endl;
    cout << tree.TreeToString();
    cout << "\nThe header of the frequent pattern tree" << endl;
    cout << tree.HeaderToString();
  }

  // Mine Frequent Patterns from tree.
  Itemsets<string> results;
  if (FLAGS_min_support_ratio >= 0) {
    tree.GetFrequentItemsets(-1, FLAGS_min_support_ratio, &results);
  } else {
    tree.GetFrequentItemsets(FLAGS_min_support_count, -1, &results);
  }

  // Output all the frequent itemsets given the min_support
  cout << "\nThe result itemsets" << endl;
  cout << results.ToString();

  if (FLAGS_run_association_rule) {
    AssociationRules<string> rules;
    if (FLAGS_min_confidence_ratio >= 0) {
      rules.BuildRules(results, 0, -1, -1, FLAGS_min_confidence_ratio);
    } else {
      rules.BuildRules(results, 0, -1, FLAGS_min_confidence_count, -1);
    }
    cout << rules.ToDebugString();
  }
}

// Mining Frequent Patterns when the items are integers.
void IntFrequentPatternMining(const vector<string>& string_list) {
  // Construct transactions. Assume -1 does not exist in transactions.
  Tree<int> tree(-1);
  TransactionsMemoryImpl<int> transactions;
  for (int i = 0; i < string_list.size(); ++i) {
    vector<string> source;
    vector<int> transaction;
    SplitStringUsing(string_list[i], " ", &source);
    for (int j = 0; j < source.size(); ++j) {
      int value;
      if (sscanf(source[j].c_str(), "%d", &value) != 1) {
        cout << "skip unknown integer item: " << source[j] << endl;
      } else {
        transaction.push_back(value);
      }
    }
    transactions.push_back(transaction);
  }

  // Build Frequent Pattern Tree
  if (FLAGS_min_support_ratio >= 0) {
    tree.BuildTree(&transactions, -1, FLAGS_min_support_ratio);
  } else {
    tree.BuildTree(&transactions, FLAGS_min_support_count, -1);
  }

  // Print Tree information
  if (FLAGS_print_tree) {
    cout << "\nThe dictionary" << endl;
    cout << tree.DictionaryToString();
    cout << "\nThe frequent pattern tree" << endl;
    cout << tree.TreeToString();
    cout << "\nThe header of the frequent pattern tree" << endl;
    cout << tree.HeaderToString();
  }

  // Mine Frequent Patterns from Tree.
  Itemsets<int> results;
  if (FLAGS_min_support_ratio >= 0) {
    tree.GetFrequentItemsets(-1, FLAGS_min_support_ratio, &results);
  } else {
    tree.GetFrequentItemsets(FLAGS_min_support_count, -1, &results);
  }

  // Output all the frequent itemsets given the min_support
  cout << "\nThe result itemsets" << endl;
  cout << results.ToString();

  if (FLAGS_run_association_rule) {
    AssociationRules<int> rules;
    if (FLAGS_min_confidence_ratio >= 0) {
      rules.BuildRules(results, 0, -1, -1, FLAGS_min_confidence_ratio);
    } else {
      rules.BuildRules(results, 0, -1, FLAGS_min_confidence_count, -1);
    }
    cout << rules.ToDebugString();
  }
}

void ProcessRequest() {
  // Process data source
  vector<string> string_list;
  string_list.push_back("this is a t-shirt");
  string_list.push_back("that is also a t-shirt");
  string_list.push_back("I want a t-shirt");
  string_list.push_back("t-shirt is good");

  // List the input itemsets
  if (FLAGS_print_input) {
    cout << "\nThe itemsets (transactions)" << endl;
    for (int i = 0; i < string_list.size(); ++i) {
      cout << StringPrintf("%d", i) << " : " << string_list[i] << endl;
    }
  }

  // Deal with two kinds of items: string & integer.
  if (FLAGS_item_type == "string") {
    StringFrequentPatternMining(string_list);
  } else {
    IntFrequentPatternMining(string_list);
  }
}

}  // namespace frequent_pattern_mining

int main(int argc, char **argv) {
  frequent_pattern_mining::ProcessRequest();
  return 0;
}
